package repositories

import (
	"context"
	"github.com/pkg/errors"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/request"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/response"
	"go.didapinche.com/time"
	"gorm.io/gorm"
	"strconv"
)

type AlertHistoryRepository interface {
	Create(ctx context.Context, history *models.AlertHistory) error
	Update(ctx context.Context, history *models.AlertHistory) error
	Delete(ctx context.Context, id []uint64) error
	UpdateByID(ctx context.Context, ids []uint64, user string) error
	Find(ctx context.Context, req *request.AlertHistoryReq) ([]*models.AlertHistory, error)
	DeleteByCreate(ctx context.Context, graphID uint64, startTime time.Time) error
	FindByRepeat(ctx context.Context) ([]*models.AlertHistory, error)
	FindApiByDate(ctx context.Context, request *request.ApiSlaRequest) ([]*response.ApiData, error)
	FindByDateGroupApp(ctx context.Context, ids []uint64, startTime, endTime string) ([]*response.History, error)
	FindByGraphID(ctx context.Context, graphID uint64, start time.Time, end time.Time, pageNum int, pageSize int) ([]*models.AlertHistory, int64, error)
	FindApiSumSla(ctx context.Context, start, end time.Time, apiIDs []uint64, tp int) (*response.ApiData, error)
	FindStatHistory(ctx context.Context, appID, templateID uint64, start time.Time, end time.Time, pageNum int, pageSize int) ([]*models.AlertHistory, int64, error)
	FindSlaByLevel(ctx context.Context, groups []string, templateIDs []uint64, start, end time.Time) ([]*response.StatItem, error)
	FindAlertCountByAppId(ctx context.Context, appID uint64, start, end time.Time) ([]*models.AlertHistory, error)
	FindSlaByLevelGroup(ctx context.Context, level int, templateIDs []uint64, start, end time.Time) ([]*response.StatGroup, error)
	FindSlaByGroup(ctx context.Context, ids []uint64, start, end time.Time) ([]*response.StatGroup, error)
	FindByDateAndAppIDsGroupApp(ctx context.Context, appIDS, templateIDs []uint64, startTime, endTime string) ([]*response.History, error)
	FindByID(ctx context.Context, ID uint64) (*models.AlertHistory, error)
	FindByDateAndID(ctx context.Context, id, kind uint64, start, end time.Time, pageNum int, pageSize int) ([]*models.AlertHistory, int64, error)
	FindByGraphIDAndStart(ctx context.Context, graphID uint64, start time.Time) (*models.AlertHistory, error)
}

type alertHistoryRepository struct {
	db *gorm.DB
}

func NewAlertHistoryRepository(db *gorm.DB) AlertHistoryRepository {
	return &alertHistoryRepository{
		db: db,
	}
}

func (h *alertHistoryRepository) Create(ctx context.Context, history *models.AlertHistory) error {
	db := h.db.Begin()
	if err := db.WithContext(ctx).Table(models.AlertHistoryTableName).Create(history).Error; err != nil {
		db.Rollback()
		return err
	}
	db.Commit()
	return nil
}

func (h *alertHistoryRepository) Update(ctx context.Context, history *models.AlertHistory) error {
	return h.db.WithContext(ctx).Table(models.AlertHistoryTableName).Save(history).Error
}

func (h *alertHistoryRepository) UpdateByID(ctx context.Context, ids []uint64, user string) error {
	return h.db.WithContext(ctx).Table(models.AlertHistoryTableName).Where("id in ?", ids).Updates(&models.AlertHistory{ClaimStatus: 1, ClaimUser: user}).Error
}
func (h *alertHistoryRepository) FindByGraphID(ctx context.Context, graphID uint64, start time.Time, end time.Time, pageNum int, pageSize int) ([]*models.AlertHistory, int64, error) {
	var count int64
	historys := make([]*models.AlertHistory, 0)
	if err := h.db.WithContext(ctx).Table(models.AlertHistoryTableName).Where("graph_id = ? and (start_time between ? and ?) ", graphID, start, end).Count(&count).Offset((pageNum - 1) * pageSize).Limit(pageSize).Scan(&historys).Error; err != nil {
		return nil, 0, errors.Wrap(err, "FindByAppIDAndTemplateID history error")
	}
	return historys, count, nil
}

func (h *alertHistoryRepository) FindByID(ctx context.Context, id uint64) (*models.AlertHistory, error) {
	alert := new(models.AlertHistory)
	if err := h.db.WithContext(ctx).Table(models.AlertHistoryTableName).First(&alert, "id=?", id).Error; err != nil {
		return nil, errors.Wrap(err, "call alertHistoryRepository FindByID error")
	}
	return alert, nil
}

func (h *alertHistoryRepository) FindApiByDate(ctx context.Context, request *request.ApiSlaRequest) ([]*response.ApiData, error) {
	var results []*response.ApiData

	db := h.db.WithContext(ctx).Table(models.AlertHistoryTableName).Select("api_id,sum(duration) sla").
		Where("start_time between ? and   ? and duration > 0 and kind=1 and template_id in ? and `type`=?", request.Start, request.End, request.TemplateIds, request.TemplateType-1)
	if request.ApiIDs != nil && len(request.ApiIDs) > 0 {
		db.Where("api_id in ?", request.ApiIDs)
	}
	if request.Level != 0 {
		db.Where("level =?", request.Level)
	}
	if err := db.Group("api_id").
		Order("sla desc").
		Find(&results).Error; err != nil {
		return nil, errors.Wrap(err, "find history by app_id error")
	}
	return results, nil
}

func (h *alertHistoryRepository) FindByDateGroupApp(ctx context.Context, ids []uint64, startTime, endTime string) ([]*response.History, error) {
	var results []*response.History

	if err := h.db.WithContext(ctx).
		Raw("select template_id id,app_id,sum(duration) duration,count(*) durationCount "+
			"from t_alert_history "+
			"where    template_id in ? and (start_time between ? and  ?) and duration > 0 group by app_id, template_id", ids, startTime, endTime).
		Scan(&results).Error; err != nil {
		return nil, errors.Wrap(err, "find history by app_id error")
	}
	return results, nil
}

func (h *alertHistoryRepository) FindByDateAndAppIDsGroupApp(ctx context.Context, appIDs, templateIDs []uint64, startTime, endTime string) ([]*response.History, error) {
	var results []*response.History

	if err := h.db.WithContext(ctx).Table(models.AlertHistoryTableName).Select("app_id,sum(duration)").Where("app_id in ? and template_id in ? and (start_time between ? and  ?) ", appIDs, templateIDs, startTime, endTime).
		Find(&results).Error; err != nil {
		return nil, errors.Wrap(err, "find history by app_ids error")
	}
	return results, nil
}

func (h *alertHistoryRepository) FindApiSumSla(ctx context.Context, start, end time.Time, apiIDs []uint64, tp int) (*response.ApiData, error) {
	apiData := new(response.ApiData)
	db := h.db.WithContext(ctx).Table(models.AlertHistoryTableName).Select("sum(Duration)/count(*) sla").
		Where(" (`start_time` between ? and ?) and kind=1 and type=? ", start, end, tp)
	if len(apiIDs) > 0 {
		db.Where("api_id in ?", apiIDs)
	}
	if err := db.Find(apiData).Error; err != nil {
		return nil, errors.Wrap(err, "find apiData FindByDate error")
	}
	return apiData, nil
}

func (h *alertHistoryRepository) FindStatHistory(ctx context.Context, appID, templateID uint64, start time.Time, end time.Time, pageNum int, pageSize int) ([]*models.AlertHistory, int64, error) {
	var count int64
	historys := make([]*models.AlertHistory, 0)
	if err := h.db.WithContext(ctx).Table(models.AlertHistoryTableName).Where("app_id = ? and template_id=? and (start_time between ? and ?) ", appID, templateID, start, end).Count(&count).Order("start_time desc").Offset((pageNum - 1) * pageSize).Limit(pageSize).Scan(&historys).Error; err != nil {
		return nil, 0, errors.Wrap(err, "FindByAppIDAndTemplateID history error")
	}
	return historys, count, nil
}
func (h *alertHistoryRepository) FindSlaByLevel(ctx context.Context, groups []string, templateIDs []uint64, start, end time.Time) ([]*response.StatItem, error) {
	statItems := make([]*response.StatItem, 0)
	if err := h.db.WithContext(ctx).Raw("select level,sum(duration)/count(*) sla from t_alert_history where  (start_time between ? and ?) and dev_group_id in ?  and template_id in ?  group by level", start, end, groups, templateIDs).Scan(&statItems).Error; err != nil {
		return nil, errors.Wrap(err, "FindSlaByLevel history error")
	}
	return statItems, nil
}
func (h *alertHistoryRepository) FindAlertCountByAppId(ctx context.Context, appID uint64, start, end time.Time) ([]*models.AlertHistory, error) {
	historys := make([]*models.AlertHistory, 0)
	if err := h.db.WithContext(ctx).Table(models.AlertHistoryTableName).Find(&historys, "app_id =? and (start_time between ? and ?) and (end_time between ? and ?)", appID, start, end, start, end).Error; err != nil {
		return nil, errors.Wrap(err, "call FindAlertCountByAppId AlertHistory error")
	}
	return historys, nil
}
func (h *alertHistoryRepository) FindSlaByLevelGroup(ctx context.Context, level int, templateIDs []uint64, start, end time.Time) ([]*response.StatGroup, error) {
	query := ""
	if level != 0 {
		query = " and  level=" + strconv.Itoa(level)
	}
	groups := make([]*response.StatGroup, 0)
	if err := h.db.WithContext(ctx).Raw("select sum(duration) sla,dev_group_id,dev_group_name from t_alert_history where  (start_time between ? and ?)"+query+"   and template_id in ?  group by dev_group_id,dev_group_name", start, end, templateIDs).Scan(&groups).Error; err != nil {
		return nil, errors.Wrap(err, "FindSlaByLevelGroup history error")
	}
	return groups, nil
}

func (h *alertHistoryRepository) FindSlaByGroup(ctx context.Context, ids []uint64, start, end time.Time) ([]*response.StatGroup, error) {
	groups := make([]*response.StatGroup, 0)
	if err := h.db.WithContext(ctx).Raw("select sum(duration) sla,count(*) count, dev_group_id,dev_group_name,template_id tag from t_alert_history   where  (start_time between ? and ?) and template_id in ?  group by dev_group_id,dev_group_name,template_id", start, end, ids).Scan(&groups).Error; err != nil {
		return nil, errors.Wrap(err, "FindSlaByGroup history error")
	}
	return groups, nil
}
func (h *alertHistoryRepository) FindByDateAndID(ctx context.Context, id, kind uint64, start, end time.Time, pageNum int, pageSize int) ([]*models.AlertHistory, int64, error) {
	var count int64
	query := ""
	if kind == 0 {
		query = " and app_id=" + strconv.Itoa(int(id))
	} else if kind == 1 {
		query = " and api_id=" + strconv.Itoa(int(id))
	} else if kind == 2 {
		query = " and host_id=" + strconv.Itoa(int(id))
	} else if kind == 3 {
		query = " and dashboard_id=" + strconv.Itoa(int(id))
	}

	historys := make([]*models.AlertHistory, 0)
	if err := h.db.WithContext(ctx).Table(models.AlertHistoryTableName).Where(" (start_time between ? and ?) "+query, start, end).Count(&count).Offset((pageNum - 1) * pageSize).Limit(pageSize).Scan(&historys).Error; err != nil {
		return nil, 0, errors.Wrap(err, "FindByAppIDAndTemplateID history error")
	}
	return historys, count, nil
}

func (h *alertHistoryRepository) FindByGraphIDAndStart(ctx context.Context, graphID uint64, start time.Time) (*models.AlertHistory, error) {
	alert := new(models.AlertHistory)
	if err := h.db.WithContext(ctx).Table(models.AlertHistoryTableName).First(alert, "graph_id =? and start_time=?", graphID, start).Error; err != nil {
		return nil, err
	}
	return alert, nil
}

func (h *alertHistoryRepository) DeleteByCreate(ctx context.Context, graphID uint64, startTime time.Time) error {
	return h.db.WithContext(ctx).Table(models.AlertHistoryTableName).Where("graph_id=? and start_time=?", graphID, startTime.String()).Delete(&models.AlertHistory{}).Error
}

func (h *alertHistoryRepository) FindByRepeat(ctx context.Context) ([]*models.AlertHistory, error) {
	historys := make([]*models.AlertHistory, 0)
	if err := h.db.WithContext(ctx).Raw("select id from t_alert_history where id not in (select min(id) id from t_alert_history group by graph_id,start_time)").Scan(&historys).Error; err != nil {
		return nil, err
	}
	return historys, nil
}

func (a alertHistoryRepository) Delete(ctx context.Context, id []uint64) error {
	return a.db.WithContext(ctx).Table(models.AlertHistoryTableName).Where("id  in ?", id).Delete(&models.AlertCurrent{}).Error
}

func (a alertHistoryRepository) Find(ctx context.Context, req *request.AlertHistoryReq) ([]*models.AlertHistory, error) {
	db := a.db.WithContext(ctx).Table(models.AlertHistoryTableName).Where("start_time between ? and ?", req.StartTime, req.EndTime).Order("start_time desc")
	alerts := make([]*models.AlertHistory, 0)
	if req.AlertLevel != -1 {
		db = db.Where("alert_level=?", req.AlertLevel)
	}
	if req.ClaimStatus != -1 {
		db = db.Where("claim_status=?", req.ClaimStatus)
	}
	if req.AppID != 0 {
		if err := db.Where("app_id=?", req.AppID).Scan(&alerts).Error; err != nil {
			return nil, err
		}
		return alerts, nil
	} else if req.ApiID != 0 {
		if err := db.Where("api_id=?", req.ApiID).Scan(&alerts).Error; err != nil {
			return nil, err
		}
		return alerts, nil
	} else if req.HostID != 0 {
		if err := db.Where("host_id=?", req.HostID).Scan(&alerts).Error; err != nil {
			return nil, err
		}
		return alerts, nil
	} else if req.DashboardID != 0 {
		if err := db.Where("dashboard_id in ?", req.DashboardIDs).Scan(&alerts).Error; err != nil {
			return nil, err
		}
		return alerts, nil
	} else if req.GraphID != 0 {
		if err := db.Where("graph_id=?", req.GraphID).Scan(&alerts).Error; err != nil {
			return nil, err
		}
		return alerts, nil
	} else {
		if req.Query == "" {
			if err := db.Scan(&alerts).Error; err != nil {
				return nil, err
			}
			return alerts, nil
		} else {
			db2 := a.db
			if len(req.AppIDs) > 0 {
				db2 = db2.Or("app_id in ?", req.AppIDs)
			}
			if len(req.ApiIDs) > 0 {
				db2 = db2.Or("api_id in ?", req.ApiIDs)
			}
			if len(req.DashboardIDs) > 0 {
				db2 = db2.Or("dashboard_id in ?", req.DashboardIDs)
			}
			if len(req.HostIDs) > 0 {
				db2 = db2.Or("host_id in ?", req.HostIDs)
			}
			if err := db.Where(db2).Scan(&alerts).Error; err != nil {
				return nil, err
			}
			return alerts, nil
		}
	}
}
